/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2008, Nils Hasler, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

// Author: Nils Hasler <hasler@mpi-inf.mpg.de>
//
//         Max-Planck-Institut Informatik
//
// this implementation was inspired by gnash's gstreamer interface

//
// use GStreamer to read a video
//

#include "precomp.hpp"
#include <unistd.h>
#include <string.h>
#include <map>
#include <gst/gst.h>
#include <gst/video/video.h>
#ifdef HAVE_GSTREAMER_APP
#include <gst/app/gstappsink.h>
#include <gst/app/gstappsrc.h>
#include <gst/riff/riff-media.h>

#else
#include "gstappsink.h"
#endif

#ifdef NDEBUG
#define CV_WARN(message)
#else
#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
#endif

static bool isInited = false;
class CvCapture_GStreamer : public CvCapture {
public:
	CvCapture_GStreamer() { init(); }
	virtual ~CvCapture_GStreamer() { close(); }

	virtual bool open( int type, const char* filename );
	virtual void close();

	virtual double getProperty(int);
	virtual bool setProperty(int, double);
	virtual bool grabFrame();
	virtual IplImage* retrieveFrame(int);

protected:
	void init();
	bool reopen();
	void handleMessage();
	void restartPipeline();
	void setFilter(const char*, int, int, int);
	void removeFilter(const char* filter);
	void static newPad(GstElement* myelement,
					   GstPad*     pad,
					   gpointer    data);
	GstElement*       	pipeline;
	GstElement*       	uridecodebin;
	GstElement*       	color;
	GstElement*       	sink;

	GstBuffer*       	buffer;
	GstCaps*            caps;
	IplImage*       	frame;
};
void CvCapture_GStreamer::init() {
	pipeline = 0;
	frame = 0;
	buffer = 0;
	frame = 0;

}

void CvCapture_GStreamer::handleMessage() {
	GstBus* bus = gst_element_get_bus(pipeline);

	while (gst_bus_have_pending(bus)) {
		GstMessage* msg = gst_bus_pop(bus);

//		printf("Got %s message\n", GST_MESSAGE_TYPE_NAME(msg));

		switch (GST_MESSAGE_TYPE (msg)) {
		case GST_MESSAGE_STATE_CHANGED:
			GstState oldstate, newstate, pendstate;
			gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendstate);
//			printf("state changed from %d to %d (%d)\n", oldstate, newstate, pendstate);
			break;
		case GST_MESSAGE_ERROR: {
			GError* err;
			gchar* debug;
			gst_message_parse_error(msg, &err, &debug);

			fprintf(stderr, "GStreamer Plugin: Embedded video playback halted; module %s reported: %s\n",
					gst_element_get_name(GST_MESSAGE_SRC (msg)), err->message);

			g_error_free(err);
			g_free(debug);

			gst_element_set_state(pipeline, GST_STATE_NULL);

			break;
		}
		case GST_MESSAGE_EOS:
//			CV_WARN("NetStream has reached the end of the stream.");

			break;
		default:
//			CV_WARN("unhandled message\n");
			break;
		}

		gst_message_unref(msg);
	}

	gst_object_unref(GST_OBJECT(bus));
}

//
// start the pipeline, grab a buffer, and pause again
//
bool CvCapture_GStreamer::grabFrame() {

	if (!pipeline) {
		return false;
	}

	if (gst_app_sink_is_eos(GST_APP_SINK(sink))) {
		//printf("end of stream\n");
		return false;
	}

	if (buffer) {
		gst_buffer_unref(buffer);
	}
	handleMessage();

#ifndef HAVE_GSTREAMER_APP
	if (gst_app_sink_get_queue_length(GST_APP_SINK(sink))) {
//		printf("peeking buffer, %d buffers in queue\n",
		buffer = gst_app_sink_peek_buffer(GST_APP_SINK(sink));
	} else
#endif
	{
		buffer = gst_app_sink_pull_buffer(GST_APP_SINK(sink));
	}
	if (!buffer) {
		return false;
	}

	return true;
}

//
// decode buffer
//
IplImage* CvCapture_GStreamer::retrieveFrame(int) {
	if (!buffer) {
		return false;
	}

	if (!frame) {
		gint height, width;
		GstCaps* buff_caps = gst_buffer_get_caps(buffer);
		assert(gst_caps_get_size(buff_caps) == 1);
		GstStructure* structure = gst_caps_get_structure(buff_caps, 0);

		if (!gst_structure_get_int(structure, "width", &width) ||
				!gst_structure_get_int(structure, "height", &height)) {
			return false;
		}

		frame = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
		gst_caps_unref(buff_caps);
	}

	memcpy (frame->imageData, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE (buffer));
	//gst_data_copy_into (frame->imageData,GST_BUFFER_DATA(buffer));
	gst_buffer_unref(buffer);
	buffer = 0;
	return frame;
}

void CvCapture_GStreamer::restartPipeline() {
	CV_FUNCNAME("icvRestartPipeline");

	__BEGIN__;

	printf("restarting pipeline, going to ready\n");

	if (gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_READY) ==
			GST_STATE_CHANGE_FAILURE) {
		CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n");
		return;
	}

	printf("ready, relinking\n");

	gst_element_unlink(uridecodebin, color);
	printf("filtering with %s\n", gst_caps_to_string(caps));
	gst_element_link_filtered(uridecodebin, color, caps);

	printf("relinked, pausing\n");

	if (gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING) ==
			GST_STATE_CHANGE_FAILURE) {
		CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n");
		return;
	}

	printf("state now paused\n");

	__END__;
}

void CvCapture_GStreamer::setFilter(const char* property, int type, int v1, int v2) {

	if (!caps) {
		if (type == G_TYPE_INT) {
			caps = gst_caps_new_simple("video/x-raw-rgb", property, type, v1, NULL);
		} else {
			caps = gst_caps_new_simple("video/x-raw-rgb", property, type, v1, v2, NULL);
		}
	} else {
		printf("caps before setting %s\n", gst_caps_to_string(caps));
		if (type == G_TYPE_INT) {
			gst_caps_set_simple(caps, "video/x-raw-rgb", property, type, v1, NULL);
		} else {
			gst_caps_set_simple(caps, "video/x-raw-rgb", property, type, v1, v2, NULL);
		}
	}

	restartPipeline();
}

void CvCapture_GStreamer::removeFilter(const char* filter) {
	if (!caps) {
		return;
	}

	GstStructure* s = gst_caps_get_structure(caps, 0);
	gst_structure_remove_field(s, filter);

	restartPipeline();
}


//
// connect uridecodebin dynamically created source pads to colourconverter
//
void CvCapture_GStreamer::newPad(GstElement* uridecodebin,
								 GstPad*     pad,
								 gpointer    data) {
	GstPad* sinkpad;
	GstElement* color = (GstElement*) data;


	sinkpad = gst_element_get_static_pad (color, "sink");

	gst_pad_link (pad, sinkpad);

	gst_object_unref (sinkpad);
}

bool CvCapture_GStreamer::open( int type, const char* filename ) {
	close();
	CV_FUNCNAME("cvCaptureFromCAM_GStreamer");

	__BEGIN__;

//	teststreamer(filename);

//	return false;

	if (!isInited) {
//		printf("gst_init\n");
		gst_init (NULL, NULL);


		isInited = true;
	}
	bool stream = false;
	char* uri = NULL;
	//const char *sourcetypes[] = {"dv1394src", "v4lsrc", "v4l2src", "filesrc"};
	//printf("entered capturecreator %s\n", sourcetypes[type]);
	if  (type == CV_CAP_GSTREAMER_FILE) {
		if (!gst_uri_is_valid(filename)) {
			uri = realpath(filename, NULL);
			if (uri) {
				uri = g_filename_to_uri(uri, NULL, NULL);
			} else {
				CV_WARN("Error opening file\n");
				close();
				return false;
			}
			stream = false;
		}

		else {
			stream = true;
			uri = g_strdup (filename);
			printf("Trying to connect to stream \n");
		}
		uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
		g_object_set(G_OBJECT(uridecodebin), "uri", uri, NULL);
	}
	if (!uridecodebin) {
		close();
		return false;
	}
	color = gst_element_factory_make("ffmpegcolorspace", NULL);

#ifdef HAVE_GSTREAMER_APP
	sink = gst_element_factory_make("appsink", NULL);
	gst_app_sink_set_max_buffers (GST_APP_SINK(sink), 1);
	if (stream) {
		gst_app_sink_set_drop (GST_APP_SINK(sink), true);
	}
#else
	sink = gst_element_factory_make("opencv-appsink", NULL);
#endif
	GstCaps* caps = gst_caps_new_simple("video/x-raw-rgb",
										"red_mask",   G_TYPE_INT, 255,
										"green_mask", G_TYPE_INT, 65280,
										"blue_mask",  G_TYPE_INT, 16711680,
										NULL);
	//GstCaps *caps=gst_video_format_new_caps(GST_VIDEO_FORMAT_BGR,,368,30,1,1,1);
	gst_app_sink_set_caps(GST_APP_SINK(sink), caps);
	gst_caps_unref(caps);
	g_signal_connect(uridecodebin, "pad-added", G_CALLBACK(newPad), color);

	pipeline = gst_pipeline_new (NULL);

	gst_bin_add_many(GST_BIN(pipeline), uridecodebin, color, sink, NULL);

	switch (type) {
	case CV_CAP_GSTREAMER_V4L2: // default to 640x480, 30 fps
		break;
	case CV_CAP_GSTREAMER_V4L:
	case CV_CAP_GSTREAMER_1394:
		break;
	}
	if (!gst_element_link(color, sink)) {
		CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n");
		gst_object_unref(pipeline);
		return false;
	}

//	printf("linked, pausing\n");

	if (gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_READY) ==
			GST_STATE_CHANGE_FAILURE) {
		CV_WARN("GStreamer: unable to set pipeline to paused\n");
//		icvHandleMessage(capture);
//		cvReleaseCapture((CvCapture **)(void *)&capture);
		gst_object_unref(pipeline);
		return false;
	}

	if (gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING) ==
			GST_STATE_CHANGE_FAILURE) {
		CV_WARN("GStreamer: unable to set pipeline to paused\n");
//		icvHandleMessage(capture);
//		cvReleaseCapture((CvCapture **)(void *)&capture);
		gst_object_unref(pipeline);
		return false;
	}



	handleMessage();

	__END__;

	return true;
}
#ifdef HAVE_GSTREAMER_APP
//
//
// gstreamer image sequence writer
//
//
class CvVideoWriter_GStreamer : public CvVideoWriter {
public:
	CvVideoWriter_GStreamer() { init(); }
	virtual ~CvVideoWriter_GStreamer() { close(); }

	virtual bool open( const char* filename, int fourcc,
					   double fps, CvSize frameSize, bool isColor );
	virtual void close();
	virtual bool writeFrame( const IplImage* image );
protected:
	void init();
	std::map<int, char*> encs;
	GstElement* source;
	GstElement* file;
	GstElement* enc;
	GstElement* mux;
	GstElement* color;
	GstBuffer* buffer;
	GstElement* pipeline;
	int input_pix_fmt;
};

void CvVideoWriter_GStreamer::init() {
	encs[CV_FOURCC('H', 'F', 'Y', 'U')] = (char*)"ffenc_huffyuv";
	encs[CV_FOURCC('D', 'R', 'A', 'C')] = (char*)"diracenc";
	encs[CV_FOURCC('X', 'V', 'I', 'D')] = (char*)"xvidenc";
	encs[CV_FOURCC('X', '2', '6', '4')] = (char*)"x264enc";
	encs[CV_FOURCC('M', 'P', '1', 'V')] = (char*)"mpeg2enc";
	//encs[CV_FOURCC('M','P','2','V')]=(char*)"mpeg2enc";
	pipeline = 0;
	buffer = 0;
}
void CvVideoWriter_GStreamer::close() {
	if (pipeline) {
		gst_app_src_end_of_stream(GST_APP_SRC(source));
		gst_element_set_state (pipeline, GST_STATE_NULL);
		gst_object_unref (GST_OBJECT (pipeline));
	}
}
bool CvVideoWriter_GStreamer::open( const char* filename, int fourcc,
									double fps, CvSize frameSize, bool is_color ) {
	CV_FUNCNAME("CvVideoWriter_GStreamer::open");

	__BEGIN__;
	//actually doesn't support fourcc parameter and encode an avi with jpegenc
	//we need to find a common api between backend to support fourcc for avi
	//but also to choose in a common way codec and container format (ogg,dirac,matroska)
	// check arguments

	assert (filename);
	assert (fps > 0);
	assert (frameSize.width > 0  &&  frameSize.height > 0);
	std::map<int, char*>::iterator encit;
	encit = encs.find(fourcc);
	if (encit == encs.end()) {
		CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer Opencv backend doesn't support this codec acutally.");
	}
	if (!isInited) {
		gst_init (NULL, NULL);
		isInited = true;
	}
	close();
	source = gst_element_factory_make("appsrc", NULL);
	file = gst_element_factory_make("filesink", NULL);
	enc = gst_element_factory_make(encit->second, NULL);
	mux = gst_element_factory_make("avimux", NULL);
	color = gst_element_factory_make("ffmpegcolorspace", NULL);
	if (!enc) {
		CV_ERROR( CV_StsUnsupportedFormat, "Your version of Gstreamer doesn't support this codec acutally or needed plugin missing.");
	}
	g_object_set(G_OBJECT(file), "location", filename, NULL);
	pipeline = gst_pipeline_new (NULL);
	GstCaps* caps;
	if (is_color) {
		input_pix_fmt = 1;
		caps = gst_video_format_new_caps(GST_VIDEO_FORMAT_BGR,
										 frameSize.width,
										 frameSize.height,
										 fps,
										 1,
										 1,
										 1);
	} else  {
		input_pix_fmt = 0;
		caps = gst_caps_new_simple("video/x-raw-gray",
								   "width", G_TYPE_INT, frameSize.width,
								   "height", G_TYPE_INT, frameSize.height,
								   "framerate", GST_TYPE_FRACTION, int(fps), 1,
								   "bpp", G_TYPE_INT, 8,
								   "depth", G_TYPE_INT, 8,
								   NULL);
	}
	gst_app_src_set_caps(GST_APP_SRC(source), caps);
	if (fourcc == CV_FOURCC_DEFAULT) {
		gst_bin_add_many(GST_BIN(pipeline), source, color, mux, file, NULL);
		if (!gst_element_link_many(source, color, enc, mux, file, NULL)) {
			CV_ERROR(CV_StsError, "GStreamer: cannot link elements\n");
		}
	} else {
		gst_bin_add_many(GST_BIN(pipeline), source, color, enc, mux, file, NULL);
		if (!gst_element_link_many(source, color, enc, mux, file, NULL)) {
			CV_ERROR(CV_StsError, "GStreamer: cannot link elements\n");
		}
	}


	if (gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING) ==
			GST_STATE_CHANGE_FAILURE) {
		CV_ERROR(CV_StsError, "GStreamer: cannot put pipeline to play\n");
	}
	__END__;
	return true;
}
bool CvVideoWriter_GStreamer::writeFrame( const IplImage* image ) {

	CV_FUNCNAME("CvVideoWriter_GStreamer::writerFrame");

	__BEGIN__;
	if (input_pix_fmt == 1) {
		if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U) {
			CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3.");
		}
	} else if (input_pix_fmt == 0) {
		if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U) {
			CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1.");
		}
	} else {
		assert(false);
	}
	int size;
	size = image->imageSize;
	buffer = gst_buffer_new_and_alloc (size);
	//gst_buffer_set_data (buffer,(guint8*)image->imageData, size);
	memcpy (GST_BUFFER_DATA(buffer), image->imageData, size);
	gst_app_src_push_buffer(GST_APP_SRC(source), buffer);
	//gst_buffer_unref(buffer);
	//buffer = 0;
	__END__;
	return true;
}
CvVideoWriter* cvCreateVideoWriter_GStreamer(const char* filename, int fourcc, double fps,
		CvSize frameSize, int isColor ) {
	CvVideoWriter_GStreamer* wrt = new CvVideoWriter_GStreamer;
	if ( wrt->open(filename, fourcc, fps, frameSize, isColor)) {
		return wrt;
	}

	delete wrt;
	return false;
}
#else
CvVideoWriter* cvCreateVideoWriter_GStreamer(const char*, int, double, CvSize, int ) {
	return false;
}

#endif
void CvCapture_GStreamer::close() {
	if (pipeline) {
		gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);
		gst_object_unref(GST_OBJECT(pipeline));
	}
	if (buffer) {
		gst_buffer_unref(buffer);
	}

	if (frame) {
		cvReleaseImage(&frame);
	}

}

double CvCapture_GStreamer::getProperty( int propId ) {
	GstFormat format;
	//GstQuery q;
	gint64 value;

	if (!pipeline) {
		CV_WARN("GStreamer: no pipeline");
		return false;
	}

	switch (propId) {
	case CV_CAP_PROP_POS_MSEC:
		format = GST_FORMAT_TIME;
		if (!gst_element_query_position(pipeline, &format, &value)) {
			CV_WARN("GStreamer: unable to query position of stream");
			return false;
		}
		return value * 1e-6; // nano seconds to milli seconds
	case CV_CAP_PROP_POS_FRAMES:
		format = GST_FORMAT_DEFAULT;
		if (!gst_element_query_position(pipeline, &format, &value)) {
			CV_WARN("GStreamer: unable to query position of stream");
			return false;
		}
		return value;
	case CV_CAP_PROP_POS_AVI_RATIO:
		format = GST_FORMAT_PERCENT;
		if (!gst_element_query_position(pipeline, &format, &value)) {
			CV_WARN("GStreamer: unable to query position of stream");
			return false;
		}
		return ((double) value) / GST_FORMAT_PERCENT_MAX;
	case CV_CAP_PROP_FRAME_WIDTH:
	case CV_CAP_PROP_FRAME_HEIGHT:
	case CV_CAP_PROP_FPS:
	case CV_CAP_PROP_FOURCC:
		break;
	case CV_CAP_PROP_FRAME_COUNT:
		format = GST_FORMAT_DEFAULT;
		if (!gst_element_query_duration(pipeline, &format, &value)) {
			CV_WARN("GStreamer: unable to query position of stream");
			return false;
		}
		return value;
	case CV_CAP_PROP_FORMAT:
	case CV_CAP_PROP_MODE:
	case CV_CAP_PROP_BRIGHTNESS:
	case CV_CAP_PROP_CONTRAST:
	case CV_CAP_PROP_SATURATION:
	case CV_CAP_PROP_HUE:
	case CV_CAP_PROP_GAIN:
	case CV_CAP_PROP_CONVERT_RGB:
		break;
	default:
		CV_WARN("GStreamer: unhandled property");
		break;
	}
	return false;
}

bool CvCapture_GStreamer::setProperty( int propId, double value ) {
	GstFormat format;
	GstSeekFlags flags;

	if (!pipeline) {
		CV_WARN("GStreamer: no pipeline");
		return false;
	}

	switch (propId) {
	case CV_CAP_PROP_POS_MSEC:
		format = GST_FORMAT_TIME;
		flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);
		if (!gst_element_seek_simple(GST_ELEMENT(pipeline), format,
									 flags, (gint64) (value * GST_MSECOND))) {
			CV_WARN("GStreamer: unable to seek");
		}
		break;
	case CV_CAP_PROP_POS_FRAMES:
		format = GST_FORMAT_DEFAULT;
		flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);
		if (!gst_element_seek_simple(GST_ELEMENT(pipeline), format,
									 flags, (gint64) value)) {
			CV_WARN("GStreamer: unable to seek");
		}
		break;
	case CV_CAP_PROP_POS_AVI_RATIO:
		format = GST_FORMAT_PERCENT;
		flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);
		if (!gst_element_seek_simple(GST_ELEMENT(pipeline), format,
									 flags, (gint64) (value * GST_FORMAT_PERCENT_MAX))) {
			CV_WARN("GStreamer: unable to seek");
		}
		break;
	case CV_CAP_PROP_FRAME_WIDTH:
		if (value > 0) {
			setFilter("width", G_TYPE_INT, (int) value, 0);
		} else {
			removeFilter("width");
		}
		break;
	case CV_CAP_PROP_FRAME_HEIGHT:
		if (value > 0) {
			setFilter("height", G_TYPE_INT, (int) value, 0);
		} else {
			removeFilter("height");
		}
		break;
	case CV_CAP_PROP_FPS:
		if (value > 0) {
			int num, denom;
			num = (int) value;
			if (value != num) { // FIXME this supports only fractions x/1 and x/2
				num = (int) (value * 2);
				denom = 2;
			} else {
				denom = 1;
			}

			setFilter("framerate", GST_TYPE_FRACTION, num, denom);
		} else {
			removeFilter("framerate");
		}
		break;
	case CV_CAP_PROP_FOURCC:
	case CV_CAP_PROP_FRAME_COUNT:
	case CV_CAP_PROP_FORMAT:
	case CV_CAP_PROP_MODE:
	case CV_CAP_PROP_BRIGHTNESS:
	case CV_CAP_PROP_CONTRAST:
	case CV_CAP_PROP_SATURATION:
	case CV_CAP_PROP_HUE:
	case CV_CAP_PROP_GAIN:
	case CV_CAP_PROP_CONVERT_RGB:
		break;
	default:
		CV_WARN("GStreamer: unhandled property");
	}
	return false;
}
CvCapture* cvCreateCapture_GStreamer(int type, const char* filename ) {
	CvCapture_GStreamer* capture = new CvCapture_GStreamer;

	if ( capture->open( type, filename )) {
		return capture;
	}

	delete capture;
	return false;
}
